package cn.lanqiao.dao;

import cn.lanqiao.annatation.ColumnName;
import cn.lanqiao.entity.User;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 代码优化
 * <p>
 * 1、抽取方法 - 各个方法中，相同的代码【完成一个业务】
 * 2、抽取带参数的方法 - 各个方法中，大量相同的代码【完成一个业务】
 * 3、使用父类优化
 */
public abstract class BaseDao<T> {

    String driver = "com.mysql.cj.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/test";
    String user = "root";
    String password = "123";

    Class<T> clazz;

    //反射获得clazz
    public BaseDao() {
        clazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Connection getConnection() {
        Connection conn = null;
        try {
            //1. 注册驱动 (仅仅做一次)
            Class.forName(driver);
            //2. 建立连接(Connection)
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

    public void closeAll(ResultSet rs, PreparedStatement statement, Connection conn) {
        //6. 释放资源
        try {
            rs.close();
            statement.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void closeAll(PreparedStatement statement, Connection conn) {
        //6. 释放资源
        try {
            statement.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * ORM
     * class <-> table
     * field <-> column
     * object <-> row
     */
    // insert into table (columns) values (?,?,?)
    // Use user;
    public List<T> executeQuery(String sql, Object... objects) {
        Connection conn = null;
        PreparedStatement statement = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<>();
        try {
            conn = getConnection();
            //3. 创建运行SQL的语句(Statement)
            statement = conn.prepareStatement(sql);
            for (int i = 0; i < objects.length; i++) {
                statement.setObject(i + 1, objects[i]);
            }
            //4. 运行语句
            rs = statement.executeQuery();
            //5. 处理运行结果(ResultSet) DML不需要此步骤
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            while (rs.next()) {
                //生成一个对象
                T t = clazz.getDeclaredConstructor().newInstance();
                //给对象的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    Field field = null;
                    try {
                        field = clazz.getDeclaredField(rsmd.getColumnLabel(i + 1));//属性名字
                    } catch (NoSuchFieldException e) {
                        // 读注解，实现数据插入 username -> userName
                        Field[] fields = clazz.getDeclaredFields();
                        for (Field f : fields) {
                            if (f.isAnnotationPresent(ColumnName.class) && f.getAnnotation(ColumnName.class).value().equals(rsmd.getColumnLabel(i + 1))) {
                                // 这里处理被注解的字段
                                field = f;
                                break;
                            }
                        }
                    }
                    if(field == null){
//                        throw new RuntimeException(rsmd.getColumnLabel(i + 1) + "字段无法获取！");
                        continue;
                    }
                    field.setAccessible(true);
                    field.set(t, rs.getObject(rsmd.getColumnLabel(i + 1)));
                }
                //将对象存入list
                list.add(t);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeAll(rs, statement, conn);
        }
        return list;
    }


    public int executeUpdate(String sql, Object... objects) {
        Connection conn = null;
        PreparedStatement statement = null;
        int ret = -1;
        try {
            //1. 注册驱动 (仅仅做一次)
            Class.forName(driver);
            //2. 建立连接(Connection)
            conn = DriverManager.getConnection(url, user, password);
            //3. 创建运行SQL的语句(Statement)
            statement = conn.prepareStatement(sql);
            for (int i = 0; i < objects.length; i++) {
                statement.setObject(i + 1, objects[i]);
            }
            //4. 运行语句
            ret = statement.executeUpdate();
            //5. 处理运行结果(ResultSet) DML不需要此步骤
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6. 释放资源
            try {
                statement.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }
}
